Если кто вдруг не знает, Google Closure сильно полагается на JSDoc-комментарии при компиляции и статическом анализе JavaScript-кода. При этом используется не совсем стандартный синтаксис этих комментариев. По крайней мере набор проверяемых инструкций сильно расширен. Кроме того, введена целая система описания типов, которая лучше отражает динамическую структуру JavaScript. Для тех, кто работает с Google Closure с завидным постоянством, всё это не представляет никакой проблемы. Но для тех, кто, как я, возвращается к нему лишь время от времени, периодически приходится заглядывать в руководство. И в помощь таким людям (и не в последнюю очередь себе) я сделал совсем небольшую шпаргалку, чтобы распечатать и повесить на стену перед глазами. Надеюсь, кому-нибудь пригодится.
Скачать pdf (65 Кб, 1 стр.)
Возможно, я ее когда-нибудь расширю, когда придумаю, чего туда добавить, а пока принимаю комментарии, предложения и благодарности.
Итак, у вас очень много JavaScript-кода. А значит, вы его сжимаете перед тем, как отдавать клиенту. Наверняка даже используете YUI Compressor или Google Closure Compiler. А может, вы пишете свой client-side код на чем-нибудь модном: на Dart или CoffeeScript? В любом случае вы наверняка сталкивались с проблемой отладки и поиска ошибки в браузере. Попробуй угадать, почему в 3-й строчке на 100501 позиции значение переменной aAz
вдруг стало undefined
. А ведь до компиляции скрипта всё работало как надо! Я в таких случаях начинаю добавлять в код много инструкций console.log
, чтобы хоть как-то проследить процесс выполнения кода.
Ну что ж, у меня для вас хорошие новости. В Google знают о ваших проблемах и даже придумали решение — source map. Идея проста: компилятор должен создавать специальный файл с информацией о соответствии скомпилированного кода не скомпилированному, а браузер должен брать информацию из этого файла и показывать красоту вместо абракадабры.
Расскажу, какие шаги мне пришлось проделать, чтобы source map заработал в этом блоге. Сразу предупреждаю, что на рабочем сайте вы его не найдете, он существует только на моей локальной машине, но вы можете попробовать собрать всё из исходников сами.
- Включаем генерацию source map в Google Closure Compiler. Для этого используется опция компилятора
--create_source_map=./script.js.map
. - Файл получили. Но в нем скорее всего прописаны неправильные пути к исходным файлам, особенно если ваш корень сервера не совпадает с корнем проекта. Придется их поправить, например, вот такой unix-командой:
sed -i 's/"static\//"\//g' static/js/script.js.map
. - Чтобы браузер знал о наличии файла с картой, нужно добавить комментарий в конец скомпилированного js-фала:
echo "//@ sourceMappingURL=script.js.map" >> static/js/script.testing.js
. - Ну и последний этап, нужно включить поддержку source map в Google Chrome. Для этого открываем Developer Tools (Ctrl+Shift+I), нажимаем на кнопку настроек в правом нижнем углу и ставим галочку напротив «Enable source maps».
Вот и всё, теперь можно отлаживать свои исходники даже несмотря на то, что у нас в браузер загружен упакованный файл.
А теперь ложка дегтя. Она одна, но зато большая: поддержка. Как это обычно бывает с новыми технологиями, не все успевают или не все хотят реализовывать всякие экспериментальные поделки. Поэтому мы будем довольствоваться только одним браузером — Google Chrome. Ребята из Mozilla тоже трудятся над поддержкой source maps, но работающего результата у них пока нет. А вот запрос «ie source maps» ожидаемо выдает 0 релевантных результатов.
Та же беда с Source Maps и в языках и компиляторах. Я нашел поддержку только в Google Closure Compiler и Dart, а так же зачатки в CoffeeScript. Если знаете еще кого-нибудь, кто поддерживает — добро пожаловать в комментарии.
Скажу, что спецификация так же предусматривает source map и для CSS файлов, но я пока не тестировал эту возможность.
Ну и напоследок пара ссылок: на спецификацию и на обзор.
Вы же помните предыдущий пост про библиотеку для создания объектов на JavaScript? Так вот, я начал разбираться, откуда браузер может брать имена для классов. Как и ожидалось, разработчики браузеров даже и не пытались создать какой-нибудь общий стандарт, и каждый работает так, как ему хочется. Мне удалось обнаружить 2 способа задания имени класса (или 3, смотря как считать). Я, конечно же, могу заблуждаться, и вполне себе возможно существование каких-нибудь других недокументированных вариантов задания имени класса. В комментариях можно мне про это сказать.
Лучше всего дела обстоят у Chrome. Кто бы мог подумать? Он берет имя из функции конструктора вне зависимости от вида записи.
some.namespace.ClassA = function () {};
console.log(new some.namespace.ClassA()); // some.namespace.ClassA
function ClassB () {}
console.log(new ClassB()); // ClassB
var ClassC = function ClassD () {}
console.log(new ClassC()); // ClassD
console.log(new ClassD()); // error: undefined
Вслед за ним идет Firefox с Firebug. Он понимает только указание имени после слова function.
some.namespace.ClassA = function () {};
console.log(new some.namespace.ClassA()); // Object
function ClassB () {}
console.log(new ClassB()); // ClassB
var ClassC = function ClassD () {}
console.log(new ClassC()); // ClassD
console.log(new ClassD()); // error: undefined
С IE и Opera дела обстоят одинаково. Они подписывают только названия встроенных объектов.
some.namespace.ClassA = function () {};
console.log(new some.namespace.ClassA()); // Object
function ClassB () {}
console.log(new ClassB()); // Object
var ClassC = function ClassD () {}
console.log(new ClassC()); // Object
console.log(new ClassD()); // error: undefined
Всё это наводит на мысль, что простым и очевидным способом создавать объекты не получится. Сначала нам понадобится определить браузеров, так как каких-то конкретных доступных фич в данном случае нет. А поскольку единственная возможность задать имя класса — напрямую его указать, то eval
будет нашим лучшим другом. И уже из этого последует тонкая настройка Google Closure Compiler, чтобы он не трогал важные для выполнения скрипта переменные.
Поделюсь секретом: создание классов уже работает, и в скором времени, после еще некоторых правок, я выложу версию 0.1 библиотеки на Github.
Подумал, что в последнее время я не использую никаких методов для создания классов в JavaScript, а пишу всё в ручную. И решил, что неплохо бы объединить всё, что мне нужно, в одну небольшую библиотеку. Чего хочется:
- Компиляция с Google Closure Compiler в режиме ADVANCED_OPTIMIZATIONS.
- В веб-инспекторе браузера (а я ориентирую в первую очередь на Google Chrome) должно быть видно всю структуру наследования. Чтобы у прототипа писался тип
ParentClass
, а не неведомая хрень вродеfn.a.createClass
. - Возможность задавать свойства с сеттерами и геттерами.
- Возможность вешать обработчики на изменение свойств.
- Возможность задания статических членов класса.
- Очень хочется, чтобы в определении класса можно было написать
singleton: true
и у него появился методgetInstance
и стало невозможно напрямую вызвать конструктор.
Может, я слишком многого хочу? А еще может, кто-нибудь уже знает подобную библиотеку? А то пока я ее напишу…
Список хотелок может расшириться в ближайшем будущем, когда я лучше над ним подумаю.
Свершилось чудо! JetBrains услышали мои молитвы и добавили поддержку аннотаций Google Closure Compiler в свои новые продукты. Теперь редактор знает, чем отличается тип параметра {string}
от {!string}
и от {?string}
. Он больше не ругается за то, что я пытаюсь сравнивать {String}
cо {string}
, ну не красота ли? Ну и куча всяких других полезных плюшек, вроде полного понимания структуры наследования объектов. Ради всего этого можно поставить EAP-версию и потерпеть другие мелкие баги.